package com.startest.sms.config;

import net.sf.ehcache.CacheManager;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.io.ResourceUtils;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;

/**
 * Created by cly on 2018/12/26.
 */
@Configuration
public class ShiroConfiguration {

    private static final Logger logger = LoggerFactory.getLogger(ShiroConfiguration.class);


    /**
     * shiro处理拦截资源
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        logger.debug("---shiro拦截资源访问---");
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //自定义拦截器
        /*Map<String, Filter> filtersMap = new LinkedHashMap<String, Filter>();
        filtersMap.put("roleOrFilter", roleOrFilter());
        shiroFilterFactoryBean.setFilters(filtersMap);*/

        //登录
        shiroFilterFactoryBean.setLoginUrl("/login.html");
        //首页
        shiroFilterFactoryBean.setSuccessUrl("/index.html");
        //错误页面，认证不通过跳转
        shiroFilterFactoryBean.setUnauthorizedUrl("/error.html");

        //配置访问权限
        //filterChainDefinitionMap必须有序，从上到下顺序判断
        LinkedHashMap<String, String> filterChainDefinitionMap=new LinkedHashMap<>();
        //登出
        filterChainDefinitionMap.put("/logout","logout");
        //配置不会被拦截的链接
        /*filterChainDefinitionMap.put("/index.html","anon");
        filterChainDefinitionMap.put("/login.html","anon");
        filterChainDefinitionMap.put("/register.html","anon");
        filterChainDefinitionMap.put("/base/**","anon");
        filterChainDefinitionMap.put("/css/**","anon");
        filterChainDefinitionMap.put("/file/**","anon");
        filterChainDefinitionMap.put("/fw/problems.html","anon");
        filterChainDefinitionMap.put("/img/**","anon");
        filterChainDefinitionMap.put("/imgs/**","anon");
        filterChainDefinitionMap.put("/js/**","anon");
        filterChainDefinitionMap.put("/map/**","anon");
        filterChainDefinitionMap.put("/plugins/**","anon");
        filterChainDefinitionMap.put("/resource/**","anon");*/

        //认证访问
        //("/fw/**","authc,roles[business],perms[yhfw:*]")
        filterChainDefinitionMap.put("/fw/serfeedback.html","authc");
        filterChainDefinitionMap.put("/fw/**","authc,roles[business]");//管理用户问题需要business角色及yhfw:*权限
        filterChainDefinitionMap.put("/news/**","authc,roles[business]");//访问用户服务模块需要登录认证
        filterChainDefinitionMap.put("/admin/**","authc,roles[system]");
        filterChainDefinitionMap.put("/ueditor/**","authc,roles[business]");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        logger.debug("----shiro拦截资源成功----");
        return shiroFilterFactoryBean;
    }


    /**
     * 加入realm容器
     * 并添加认证器
     * @return
     */
    @Bean
    public UserRealm userRealm(){
        UserRealm userRealm = new UserRealm();
        userRealm.setCredentialsMatcher(retryLimitCredentialsMatcher());
        return userRealm;
    }
    @Bean
    public PhoneRealm phoneRealm(){
        PhoneRealm phoneRealm = new PhoneRealm();
        phoneRealm.setCredentialsMatcher(retryLimitCredentialsMatcher());
        return phoneRealm;
    }

    /**
     * shiro配置realm认证管理
     * shiro配置ehcache缓存管理
     * @return
     */
    @Bean
    public SecurityManager securityManager(){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        ArrayList<Realm> realms = new ArrayList<>();
        realms.add(userRealm());
        realms.add(phoneRealm());
        //注入realm
        securityManager.setRealms(realms);
        //注入ehcache缓存
        securityManager.setCacheManager(ehCacheManager());
        //注入session管理器
        securityManager.setSessionManager(sessionManager());
        //注入cookie管理器
        securityManager.setRememberMeManager(rememberMeManager());
        return securityManager;
    }

    /**
     * 注入ehcache缓存
     * 同时 解决热部署 ehcache重复创建CacheManager问题
     * @return
     */
    @Bean
    public EhCacheManager ehCacheManager(){
        logger.debug("----shiro注入ehcache缓存----");
        EhCacheManager ehCacheManager = new EhCacheManager();
        CacheManager cacheManager = CacheManager.getCacheManager("es");
        if(cacheManager==null){
            try {
                cacheManager = CacheManager.create(ResourceUtils.getInputStreamForPath("classpath:ehcache.xml"));
            } catch (IOException e) {
                logger.error("初始化cacheManager失败",e);
                throw new RuntimeException("initialize cacheManager failed");
            }
        }
        ehCacheManager.setCacheManager(cacheManager);
        return ehCacheManager;
    }

    /**
     * CookieRememberMeManager 记住我管理器
     * @return
     */
    @Bean
    public CookieRememberMeManager rememberMeManager(){
        logger.debug("shiro配置cookieManager记住我管理器");
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        //cookie名称；对应前端的checkbox的name = remembewMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        //设置为10天   单位秒 1天=86400秒
        simpleCookie.setMaxAge(864000);
        cookieRememberMeManager.setCookie(simpleCookie);
        return cookieRememberMeManager;
    }

    /**
     * 添加sessionManager缓存管理器操作Dao
     * @return
     */
    @Bean
    public DefaultWebSessionManager sessionManager(){
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(sessionDAO());
        sessionManager.setSessionIdCookie(sessionIdCookie());
        return sessionManager;
    }

    /**
     * shiro sessionDAO层的实现
     * 提供缓存的会话维护功能，默认情况下使用MapCache实现，内部使用ConcurrentHashMap保存缓存会话
     * @return
     */
    @Bean
    public EnterpriseCacheSessionDAO sessionDAO(){
        EnterpriseCacheSessionDAO sessionDAO = new EnterpriseCacheSessionDAO();
        sessionDAO.setActiveSessionsCacheName("shiro-activeSessionCache");
        return sessionDAO;
    }

    /**
     * 限制登录次数
     * @return
     */
    @Bean
    public CredentialsMatcher retryLimitCredentialsMatcher(){
        RetryLimitCredentialsMatcher retryLimitCredentialsMatcher = new RetryLimitCredentialsMatcher(ehCacheManager());
        retryLimitCredentialsMatcher.setMaxRetryNum(5);
        return retryLimitCredentialsMatcher;
    }

    /**
     * 自定义cookie中session名称
     * @return
     */
    @Bean
    public SimpleCookie sessionIdCookie(){
        SimpleCookie simpleCookie = new SimpleCookie();
        //如果在Cookie中设置了"HttpOnly"属性，那么通过程序(JS脚本、Applet等)将无法读取到Cookie信息，这样能有效的防止XSS攻击。
        simpleCookie.setHttpOnly(true);
        simpleCookie.setName("SHIROSESSIONID");
        //单位秒  1天=86400秒
        simpleCookie.setMaxAge(86400);
        return simpleCookie;
    }

    /**
     * 在thymeleaf里使用shiro标签
     */
   /* @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }*/

    /**
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

}
